package com.tbl.modules.wms.service.Impl.baseinfo;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.tbl.common.utils.PageTbl;
import com.tbl.common.utils.PageUtils;
import com.tbl.common.utils.Query;
import com.tbl.common.utils.StringUtils;
import com.tbl.modules.wms.constant.Constant;
import com.tbl.modules.wms.dao.baseinfo.CarDAO;
import com.tbl.modules.wms.dao.baseinfo.ShelfDAO;
import com.tbl.modules.wms.dao.baseinfo.WarehouseDAO;
import com.tbl.modules.wms.dao.productbind.DeviceUwbRouteDAO;
import com.tbl.modules.wms.dao.storageinfo.StorageInfoDAO;
import com.tbl.modules.wms.entity.baseinfo.Car;
import com.tbl.modules.wms.entity.baseinfo.Shelf;
import com.tbl.modules.wms.entity.baseinfo.Warehouse;
import com.tbl.modules.wms.entity.productbind.DeviceUwbRoute;
import com.tbl.modules.wms.entity.storageinfo.StorageInfo;
import com.tbl.modules.wms.service.baseinfo.ShelfService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 库位信息
 * @author 70486
 */
@Service("shelfService")
public class ShelfServiceImpl extends ServiceImpl<ShelfDAO, Shelf> implements ShelfService {

	/**
	 * 库位信息
	 */
    @Autowired
    private ShelfDAO shelfDAO;
	/**
	 * 库存信息
	 */
	@Autowired
    private StorageInfoDAO storageInfoDAO;
	/**
	 * 航车坐标信息
	 */
	@Autowired
	private DeviceUwbRouteDAO deviceUwbRouteDAO;
	/**
	 * 航车
	 */
	@Autowired
	private CarDAO carDAO;
	/**
	 * 仓库信息
	 */
	@Autowired
	private WarehouseDAO warehouseDAO;

    /**
     * 库位列表分页获取列表数据
     * @param pageTbl 分页
     * @param map 查询参数
     * @return PageUtils
     */
    @Override
    public PageUtils getPageList(PageTbl pageTbl, Map<String, Object> map) {
        String sortName = pageTbl.getSortname();
        String sortOrder = pageTbl.getSortorder();
        if (StringUtils.isEmptyString(sortName)) {
        	sortName = "id";
		}
        if (StringUtils.isEmptyString(sortOrder)) {
        	sortOrder = "desc";
		}
        Page page = new Page(pageTbl.getPageno(), pageTbl.getPagesize(), sortName, "asc".equalsIgnoreCase(sortOrder));
        return new PageUtils(page.setRecords(shelfDAO.getPageList(page, map)));
    }

    /**
     * 获取Excel导出列表
     * @param ids 库位主键
     * @param code 库位编码
     * @param factoryList 所属厂区
	 * @param warehouse 仓库编码
	 * @param factoryCode 厂区编码
	 * @param warehouseIds 仓库ID集合
     * @return List<Shelf> 库位列表
     */
    @Override
    public List<Shelf> getExcelList(String ids, String code, List<String> factoryList, String warehouse, String factoryCode, List<Long> warehouseIds) {
        Map<String, Object> map = new HashMap<>();
		map.put("ids", StringUtils.stringToInt(ids));
        map.put("code", code);
        map.put("factoryList", factoryList);
		map.put("warehouse", warehouse);
		map.put("factoryCode", factoryCode);
		map.put("warehouseIds",warehouseIds);
        return shelfDAO.getExcelList(map);
    }
    
    /**
     * 库位占用率列表
     * @param params 参数条件
     * @return PageUtils
     */
    @Override
    public PageUtils getList(Map<String, Object> params) {
    	Page<Shelf> page = this.selectPage(new Query<Shelf>(params).getPage(), new EntityWrapper<>());
    	List<Shelf> result = new ArrayList<>();
    	Shelf sf;
    	String warehousecode,codes;
    	String [] codesArr;
    	int t,l;
    	boolean isExist;
    	for (Shelf shelf : shelfDAO.getList(page, params)) {
    		t = l = 0;
            //仓库编码
    		warehousecode = shelf.getWarehouseCode();
            //库位编码
    		codes = shelf.getCodes();
    		codesArr = codes.split(",");
    		for (String cd : codesArr) {
				//统计该仓库编码下有几个库位
    			t++;
    			isExist=storageInfoDAO.selectCount(new EntityWrapper<StorageInfo>()
						.eq("WAREHOUSECODE",warehousecode)
						.eq("SHELFCODE",cd))>0;
				if(isExist) {
                    //统计该仓库编码下有几个库位已经使用
					l++;
				}
			}
    		sf = new Shelf();
    		sf.setWarehouseCode(warehousecode);
    		sf.setUsePro(new DecimalFormat("#0.00").format((float)l/t*100));
    		result.add(sf);
		}
    	return new PageUtils(page.setRecords(result));
    }
    
    /**
     * 库位占用率图形
     * @return Map<String,Object>
     */
    @Override
    public Map<String,Object> getLine() {
    	Map<String,Object> map = new HashMap<>(1);
    	List<Shelf> uselist = shelfDAO.getLine();
    	uselist.forEach(shelf->{
    		Long userNum = shelf.getUseNum();
    		Long totalNum = shelf.getTotalNum();
    		shelf.setUsePro(userNum!=null&&totalNum!=null?String.valueOf((float)userNum/totalNum):"");
    	});
    	map.put("uselist", uselist);
    	return map;
    }

    /**
     * 释放库位
     * @param ids 库位主键
     */
    @Override
	@Transactional(rollbackFor = Exception.class)
    public void isStart(Long[] ids) {
		Shelf shelfEntity = new Shelf();
		shelfEntity.setState(Constant.INT_ONE);
		shelfDAO.update(shelfEntity, new EntityWrapper<Shelf>().in("ID", ids)
				.ne("STATE",Constant.INT_TWO)
				.ne("STATE",199));
    }

    /**
     * 关闭库位
     * @param ids 库位主键
     */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void disMiss(Long[] ids) {
		Shelf shelfEntity = new Shelf();
		shelfEntity.setState(Constant.INT_ZERO);
		shelfDAO.update(shelfEntity, new EntityWrapper<Shelf>()
				.in("ID", ids)
				.ne("STATE", Constant.INT_TWO)
				.ne("STATE",199));
	}
	
	/**
     * 获取推荐库位
	 * 推荐规则：1.盘大小，盘类型；2.营销经理的放一块；3.靠近过道
     * @param factoryCode 厂区编码
     * @param warehouseCode 仓库编码
     * @param outerDiameter 盘外经
     * @param drumType 盘类型
	 * @param carCode 航车
     * @param moveShelf 移库入库之前所在库位
     * @param warehouseArea 仓库区域
	 * @param salesCode 营销经理编号
	 * @param superWide 是否超宽
	 * @param coloured 是否有颜色
     * @return List<Shelf> 库位列表
     */
	@Override
    public List<Shelf> selectRecommentList(String factoryCode,String warehouseCode, String outerDiameter, String drumType, String carCode,
										   String moveShelf, String warehouseArea, String salesCode, Integer superWide, Integer coloured){
		//盘类型参数转换
		if (drumType == null){
			drumType = "3";
		}else {
			switch (drumType) {
				case "铁木盘":drumType = "1";break;
				case "全木盘":drumType = "2";break;
				case "全铁盘":drumType = "3";break;
				default:drumType = "3";break;
			}
		}

		List<Shelf> returnShelfList = new ArrayList<>(), lstShelfRuleOneOk = new LinkedList<>();
        //最终推荐库位
		Shelf recommendShelf ;
        //当前挂钩所处的xy坐标
		float nowX = 0F, nowY = 0F;
		//是否是虚拟行车库（室内库）
		boolean isVisualShelf = false;

		//当前行吊的位置
		DeviceUwbRoute deviceUwbRoute = null;
		if (StringUtils.isNotBlank(carCode)){
			Car car = carDAO.selectList(new EntityWrapper<Car>().eq("CODE", carCode)).get(0);
			if (StringUtils.isNotBlank(car.getTag())){
				deviceUwbRoute = deviceUwbRouteDAO.selectList(new EntityWrapper<DeviceUwbRoute>().eq("TAG", car.getTag())).get(0);
			}else {
				isVisualShelf = true;
			}
		}
		if (deviceUwbRoute!=null){
			nowX = deviceUwbRoute.getXsize()*100;
			nowY = deviceUwbRoute.getYsize()*100;
		}

		//零头库、寄售库等室内无行车库位，不推荐库位
		if(StringUtils.isBlank(drumType)||StringUtils.isBlank(outerDiameter)||isVisualShelf){
			return returnShelfList;
		}

		//获取完全匹配的顶配推荐库位前几位的标识
		List<String> shelfTopTwoAll = shelfDAO.selectRecommentTwoCode(factoryCode, warehouseCode, "first", outerDiameter, drumType,
				Arrays.asList(warehouseArea.split(",")), superWide, coloured);
		if (shelfTopTwoAll!=null && shelfTopTwoAll.size()>0){
			List<String> shelfTopTwoFirst = shelfDAO.selectAllTwoCode(shelfTopTwoAll);
			//获取符合盘大小，盘类型规则的顶配可用库位
			if(shelfTopTwoFirst!=null && shelfTopTwoFirst.size()>0){
				lstShelfRuleOneOk = shelfDAO.selectRoleOneShelfList(factoryCode, warehouseCode, shelfTopTwoFirst);
			}
		}

		if(lstShelfRuleOneOk.size()==0) {
			//获取次顶配推荐库位前几位的标识(优先级从前往后)
			shelfTopTwoAll = shelfDAO.selectRecommentTwoCode(factoryCode, warehouseCode, "second", outerDiameter, drumType,
					Arrays.asList(warehouseArea.split(",")), superWide, coloured);
			if(shelfTopTwoAll!=null && shelfTopTwoAll.size()>0){
				List<String> shelfTopTwoSecond = shelfDAO.selectAllTwoCode(shelfTopTwoAll);
				//获取符合盘大小，盘类型规则的次顶配可用库位
				if (shelfTopTwoSecond != null && shelfTopTwoSecond.size()>0) {
					for (String str:shelfTopTwoSecond) {
						lstShelfRuleOneOk = shelfDAO.selectRoleTwoShelfList(factoryCode, warehouseCode, str);
						if (lstShelfRuleOneOk!=null && lstShelfRuleOneOk.size()>0){
							break;
						}
					}
				}
			}
		}

		//次顶配都没有了
		if (lstShelfRuleOneOk==null || lstShelfRuleOneOk.size()==0){
			return returnShelfList;
		}

		//查询货场中是否有对应营销经理的货，如果有，在附近找库位（规则二），如果没有则根据第三规则找推荐库位
		//获取距离走廊最近的营销经理库位（可能有多个）
		List<Shelf> mostClosedSalesShelfList = shelfDAO.selectMostClosedShelfList(salesCode, Arrays.asList(warehouseArea.split(",")));
		//有营销经理存在，获取对应营销经理的货物所在的库位
		if (StringUtils.isNotBlank(salesCode) && mostClosedSalesShelfList!=null && mostClosedSalesShelfList.size()>0){
			Float finalNowX = nowX;
			Float finalNowY = nowY;
			//获取最靠近挂钩的营销经理库位
			mostClosedSalesShelfList.sort((o1, o2)->
					Integer.compare((int)(Math.sqrt(Math.pow((finalNowX - o1.getMidxsize()), 2) + Math.pow((finalNowY - o1.getMidysize()), 2)) -
							Math.sqrt(Math.pow((finalNowX - o2.getMidxsize()), 2) + Math.pow((finalNowY - o2.getMidysize()), 2))),0));
			//最靠近挂钩的营销经理库位
			Shelf mostSalesClosedShelf = mostClosedSalesShelfList.get(0);
			//在盘大小盘类型符合的库位中找到距离mostSalesClosedShelf最近的空库位，就是推荐库位了
			lstShelfRuleOneOk.sort((o1, o2) ->
					Integer.compare((int)(Math.sqrt(Math.pow((mostSalesClosedShelf.getMidxsize() - o1.getMidxsize()), 2) +
					Math.pow((mostSalesClosedShelf.getMidysize() - o1.getMidysize()), 2))-
					Math.sqrt(Math.pow((mostSalesClosedShelf.getMidxsize() - o2.getMidxsize()), 2) +
					Math.pow((mostSalesClosedShelf.getMidysize() - o2.getMidysize()), 2))),0));
			//推荐库位：移库的推荐库位需要把原所在库位剔除
			recommendShelf = StringUtils.isNotEmpty(moveShelf) && moveShelf.equals(lstShelfRuleOneOk.get(0).getCode()) ?
					lstShelfRuleOneOk.get(1) : lstShelfRuleOneOk.get(0);
		}else {
            //没有营销经理库位存在，在符合盘大小，盘类型的可用库位中，找到距离过道最近的库位
			lstShelfRuleOneOk.sort((a, b) -> Integer.compare((int)(a.getDistance() - b.getDistance()),0));
			//最近的距离
			double mostCloseDistance = lstShelfRuleOneOk.get(0).getDistance();
			//最靠近过道的库位可能有多个（距离过道距离都相同）
			List<Shelf> mostClosedHallWayShelfList = lstShelfRuleOneOk.stream().filter(s ->s.getDistance() == mostCloseDistance)
					.collect(Collectors.toList());

			//在这多个中查找出距离挂钩最近的为推荐库位
			if (mostClosedHallWayShelfList.size()>1) {
				float finalNowX1 = nowX;
				float finalNowY1 = nowY;
				mostClosedHallWayShelfList.sort((o1, o2) ->
						Integer.compare((int)(Math.sqrt(Math.pow((finalNowX1 - o1.getMidxsize()), 2) + Math.pow((finalNowY1 - o1.getMidysize()), 2)) -
						Math.sqrt(Math.pow((finalNowX1 - o2.getMidxsize()), 2) + Math.pow((finalNowY1 - o2.getMidysize()), 2))),0));
				//推荐库位：移库的推荐库位需要把原所在库位剔除
				recommendShelf = StringUtils.isNotEmpty(moveShelf) && moveShelf.equals(mostClosedHallWayShelfList.get(0).getCode()) ?
						mostClosedHallWayShelfList.get(1) : mostClosedHallWayShelfList.get(0);
			}else {
				//最终的推荐库位
				recommendShelf = mostClosedHallWayShelfList.size() > 0 ? mostClosedHallWayShelfList.get(0) : lstShelfRuleOneOk.get(0);
			}
		}

		returnShelfList.add(recommendShelf);
		return returnShelfList;
	}
	
	 /**
     * 获取库位表中未被使用的库位
     * @param warehouseCode 仓库编码
     * @param factoryCode 厂区编码
     * @param warehouseArea 仓库区域
     * @return List<Shelf> 库位列表
     */
    @Override
    public List<Shelf> getRecommendShelf(String warehouseCode, String factoryCode, String warehouseArea) {
        return shelfDAO.getRecommendShelf(warehouseCode, factoryCode, Arrays.asList(warehouseArea.split(",")));
    }
    
    /**
     * 根据厂区和仓库获取选择的库位列表
     * @param pageTbl 分页工具类
     * @param map 参数条件
     * @return PageUtils
     */
    @Override
    public PageUtils getShelfByFactoryWarehouse(PageTbl pageTbl, Map<String, Object> map) {
        String sortName = pageTbl.getSortname();
        String sortOrder = pageTbl.getSortorder();
        if (StringUtils.isEmptyString(sortName)) {
        	sortName = "t1.id";
		}
        if (StringUtils.isEmptyString(sortOrder)) {
        	sortOrder = "desc";
		}
        Page page = new Page(pageTbl.getPageno(), pageTbl.getPagesize(), sortName, "asc".equalsIgnoreCase(sortOrder));
        return new PageUtils(page.setRecords(shelfDAO.getShelfByFactoryWarehouse(page, map)));
    }

	/**
	 * 根据厂区和仓库获取选择的库位列表
	 * @param pageTbl 分页工具类
	 * @param map 参数条件
	 * @return PageUtils
	 */
	@Override
	public PageUtils getShelfByFactWareCode(PageTbl pageTbl, Map<String, Object> map) {
		String sortName = pageTbl.getSortname();
		String sortOrder = pageTbl.getSortorder();
		if (StringUtils.isEmptyString(sortName)) {
			sortName = "t1.id";
		}
		if (StringUtils.isEmptyString(sortOrder)) {
			sortOrder = "desc";
		}
		Page page = new Page(pageTbl.getPageno(), pageTbl.getPagesize(), sortName, "asc".equalsIgnoreCase(sortOrder));
		return new PageUtils(page.setRecords(shelfDAO.getShelfByFactWareCode(page, map)));
	}

	/**
	 * 设置所有的库位状态为可用
	 */
	@Override
	public void setAllStatesUseful() {
		shelfDAO.setAllStatesUseful();
	}

	/**
	 * 库位设为不可用/可用
	 * @param code 库位编码
	 * @param factoryCode 厂区
	 * @param warehouseCode 仓库
	 * @param type 1可用；0不可用
	 */
	@Override
	public void setShelfState(String code, String factoryCode, String warehouseCode, Integer type) {
		List<Shelf> lstShelf = shelfDAO.selectList(new EntityWrapper<Shelf>()
				.eq("CODE", code)
				.eq("FACTORYCODE",factoryCode)
				.eq("WAREHOUSECODE",warehouseCode));
		if (lstShelf.size()>0){
			Shelf shelf = lstShelf.get(0);
			List<Warehouse> lstWarehouse = warehouseDAO.selectList(new EntityWrapper<Warehouse>()
					.eq("CODE", shelf.getWarehouseCode())
					.eq("FACTORYCODE", shelf.getFactoryCode()));
			if (lstWarehouse.size()>0){
				Warehouse warehouse = lstWarehouse.get(0);
				//判断是否复用，true复用，false不复用
				boolean repeatOccupancyOrNot = warehouse.getRepeatOccupancy()!=0 || shelf.getRepeatOccupancy()!=0;

				if (!repeatOccupancyOrNot && shelf.getState()!=199){
					shelf.setState(type);
					shelfDAO.updateById(shelf);
				}
			}
		}
	}

	/**
	 * 根据仓库区域获取可用库位
	 * @param warehouseAreaList 仓库区域列表
	 * @return Shelf 库位集合
	 */
	@Override
	public List<Shelf> findShelfByArea(List<String> warehouseAreaList){
		return shelfDAO.findShelfByArea(warehouseAreaList);
	}

	/**
	 * 根据行车编码获取所有库位
	 * @param warehouseAreaList 仓库区域列表
	 * @return Shelf 库位集合
	 */
	@Override
	public List<Shelf> findAllShelfByArea(List<String> warehouseAreaList){
		return shelfDAO.findAllShelfByArea(warehouseAreaList);
	}

	/**
	 * 根据主键查询库位信息
	 * @param id 库位主键
	 * @param factoryCode 厂区编码
	 * @return Shelf 库位信息
	 */
	@Override
	public Shelf findShelfById(Long id,String factoryCode) {
		return id == null || id==0L ? new Shelf() : shelfDAO.findShelfById(id,factoryCode);
	}
}
